/*  
 * Actipro Software's WPF DataGrid / Editors Integration Add-on
 * http://actipro.codeplex.com/
 * 
 * Copyright (c) 2009-2015 Actipro Software LLC. 
 *  
 * This source code is subject to the terms of the Microsoft Public License (Ms-PL). 
 *  
 * Redistribution and use in source and binary forms, with or without modification, 
 * is permitted provided that redistributions of the source code retain the above 
 * copyright notices and this file header. 
 *  
 * Additional copyright notices should be appended to the list above. 
 * 
 * For details, see <http://www.opensource.org/licenses/ms-pl.html>. 
 *  
 * All other rights reserved. 
 *
 */  


using System;
using System.Diagnostics.CodeAnalysis;
using System.Windows;
using System.Windows.Controls;
using ActiproSoftware.Windows.Controls.Editors.Primitives;

namespace ActiproSoftware.Windows.Controls.Editors.Interop.DataGrid.Primitives {

	/// <summary>
	/// Represents a base class for data-bound columns for use in a <c>DataGrid</c> that utilizes <see cref="TypeSpecificEditBoxBase{T}"/>-derived controls.
	/// </summary>
	public abstract class DataGridTypeSpecificEditBoxColumnBase<T> : DataGridPartEditBoxColumnBase {

		#region Dependency Properties

		/// <summary>
		/// Identifies the <see cref="PartValueCommitTriggers"/> dependency property.  This field is read-only.
		/// </summary>
		/// <value>The identifier for the <see cref="PartValueCommitTriggers"/> dependency property.</value>
		[SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
		public static readonly DependencyProperty PartValueCommitTriggersProperty = TypeSpecificEditBoxBase<T>.PartValueCommitTriggersProperty.AddOwner(typeof(DataGridTypeSpecificEditBoxColumnBase<T>),
			new FrameworkPropertyMetadata(PartValueCommitTriggers.SpinnerChange | PartValueCommitTriggers.StringValueChange, OnNotifyablePropertyValueChanged));

		#endregion // Dependency Properties

		/////////////////////////////////////////////////////////////////////////////////////////////////////
		#region PUBLIC PROCEDURES
		/////////////////////////////////////////////////////////////////////////////////////////////////////

		/// <summary>
		/// Applies standard values to the specified target element.
		/// </summary>
		/// <param name="targetElement">The target element.</param>
		protected override void ApplyStandardValues(FrameworkElement targetElement) {
			if (targetElement is TypeSpecificEditBoxBase<T>) {
				this.ApplyValue(PartValueCommitTriggersProperty, targetElement, TypeSpecificEditBoxBase<T>.PartValueCommitTriggersProperty);
			}
			base.ApplyStandardValues(targetElement);
		}

		/// <summary>
		/// Cancels the cell edit.
		/// </summary>
		/// <param name="editingElement">The editing element.</param>
		/// <param name="uneditedValue">The unedited value.</param>
		protected override void CancelCellEdit(FrameworkElement editingElement, object uneditedValue) {
			UpdateBindingTarget(editingElement as TypeSpecificEditBoxBase<T>, TypeSpecificEditBoxBase<T>.ValueProperty);
		}

		/// <summary>
		/// Commits the cell edit.
		/// </summary>
		/// <param name="editingElement">The editing element.</param>
		/// <returns></returns>
		protected override bool CommitCellEdit(FrameworkElement editingElement) {
			TypeSpecificEditBoxBase<T> editBox = editingElement as TypeSpecificEditBoxBase<T>;
			if (null != editBox) {
				UpdateBindingSource(editBox, TypeSpecificEditBoxBase<T>.ValueProperty);
				return !Validation.GetHasError(editBox);
			}
			return true;
		}

		/// <summary>
		/// Generates an instance of a <see cref="PartEditBox"/>-derived object.
		/// </summary>
		/// <param name="isEditing">if set to <c>true</c> the edit box will be used for editing.</param>
		/// <param name="cell">The cell.</param>
		/// <returns>
		/// An instance of a <see cref="PartEditBox"/>-derived object.
		/// </returns>
		protected override PartEditBox GenerateEditBox(bool isEditing, DataGridCell cell) {
			Type type = this.GetEditBoxType();

			TypeSpecificEditBoxBase<T> editBox = null;
			if (null != cell) {
				if (type.IsInstanceOfType(cell.Content))
					editBox = (TypeSpecificEditBoxBase<T>)cell.Content;
			}

			if (null == editBox)
				editBox = (TypeSpecificEditBoxBase<T>)Activator.CreateInstance(type);

			this.ApplyStandardValues(editBox);
			if (isEditing)
				editBox.Style = this.EditingElementStyle;
			else
				editBox.Style = this.ElementStyle;
			this.ApplyBinding(editBox, TypeSpecificEditBoxBase<T>.ValueProperty);
			return editBox;
		}

		/// <summary>
		/// Gets the type of the associated <see cref="PartEditBox"/>-derived control.
		/// </summary>
		/// <returns>The type of the associated <see cref="PartEditBox"/>-derived control.</returns>
		[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
		protected abstract Type GetEditBoxType();

		/// <summary>
		/// Gets the value associated with the specified <see cref="PartEditBox"/>.
		/// </summary>
		/// <param name="editBox">The <see cref="PartEditBox"/> whose value should be returned.</param>
		/// <returns>The value associated with the specified <see cref="PartEditBox"/>.</returns>
		protected override object GetEditBoxValue(PartEditBox editBox) {
			TypeSpecificEditBoxBase<T> typeSpecificEditBox = editBox as TypeSpecificEditBoxBase<T>;
			if (null != typeSpecificEditBox)
				return typeSpecificEditBox.Value;
			return default(T);
		}

		/// <summary>
		/// Gets or sets the triggers that will force any parts to commit any changes.
		/// This is a dependency property.
		/// </summary>
		/// <value>
		/// The triggers that will force any parts to commit any changes.
		/// The default value is <c>PartValueCommitTriggers.SpinnerChange</c>.
		/// </value>
		/// <remarks>
		/// Any pending changes will always be committed when the part loses focus.
		/// </remarks>
		public PartValueCommitTriggers PartValueCommitTriggers {
			get { return (PartValueCommitTriggers)this.GetValue(DataGridTypeSpecificEditBoxColumnBase<T>.PartValueCommitTriggersProperty); }
			set { this.SetValue(DataGridTypeSpecificEditBoxColumnBase<T>.PartValueCommitTriggersProperty, value); }
		}

		#endregion // PUBLIC PROCEDURES
	}
}
